/*
 *
 *  Copyright (C) 2010 Tal Eisenberg
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package fork.nodeditor;

import fork.NodeEditor;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;
import javax.swing.UIManager;
import javax.swing.border.LineBorder;
import tal.drivechain.chain.Chain;
import tal.drivechain.chain.ChainNode;
import tal.drivechain.chain.ChainNode.MSG;
import tal.drivechain.chain.NodeListener;
import tal.drivechain.gui.ForkScrollBarUI;
import tal.drivechain.gui.GUITextBox;
import tal.drivechain.gui.TextButton;
import tal.drivechain.module.properties.ColorParser;
import tal.drivechain.module.xmlconfig.PropertyInfo;
import tal.drivechain.plugin.Channel;
import tal.drivechain.variable.VarTableListener;
import tal.drivechain.variable.Variable;

/**
 *
 * @author eisental
 */
public class NodeEditorGUI extends JPanel {

    PropsPanel propsPanel;
    JScrollPane scroll;
    JLabel classLabel = new JLabel();
    JTextField nameField = new JTextField();
    JPanel blackPanel;
    NodeEditor owner;
    TextButton debugButton, infoButton, removeButton, aboutButton, exportButton;
    GUITextBox sendMsgBox;
    JLabel sendLabel;
    Color color;
    private VarTableListener chainPropertyUpdater = new ChainPropertyUpdater();
    
    public void init(Color color, NodeEditor owner) {
        this.removeAll();
        this.setLayout(new GridBagLayout());
        this.color = ColorParser.xor(color);
        UIManager.put("ScrollBar.width", new Integer(8));

        this.owner = owner;
        classLabel.setOpaque(true);
        classLabel.setBackground(Color.BLACK);

        nameField.setOpaque(true);
        nameField.setBackground(Color.BLACK);
        Font f = owner.titleFont;
        classLabel.setFont(f.deriveFont(Font.BOLD, (float)(f.getSize()*1.1*1.1)));
        nameField.setFont(f.deriveFont((float)(f.getSize()*1.1)));

        nameField.addKeyListener(new KeyAdapter(){
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode()==KeyEvent.VK_ENTER) {
                    ChainNode node = NodeEditorGUI.this.owner.getNode();
                    if (node==null) node = NodeEditorGUI.this.owner.getActualEditChain();
                    if (!nameField.getText().equals(node.getName())) {
                        try {
                            Chain parent = node.getParentChain();
                            if (parent==null) {
                                NodeEditorGUI.this.owner.log("Can't rename root chain.", MSG.ERROR);
                                return;
                            } else
                                parent.renameChild(node, nameField.getText());
                            new Thread() {
                                @Override
                                public void run() {
                                    nameField.setForeground(ColorParser.xor(NodeEditorGUI.this.owner.getColor()));
                                    try {
                                        sleep(300);
                                        nameField.setForeground(NodeEditorGUI.this.owner.getColor());
                                    } catch (InterruptedException ex) {}
                                }
                            }.start();
                        } catch (IllegalArgumentException ie) {
                            NodeEditorGUI.this.owner.log("While renaming node: " + ie, MSG.ERROR);
                        }
                    }
                }
            }
        });

        propsPanel = new PropsPanel(color, owner);
        propsPanel.setFont(getFont());
        scroll = new JScrollPane();
        blackPanel = new JPanel();
        blackPanel.setBackground(Color.BLACK);
        scroll.setCorner(JScrollPane.LOWER_TRAILING_CORNER, blackPanel);

        scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        scroll.setViewportView(propsPanel);

        GridBagConstraints c = new GridBagConstraints();
        c.weighty = 0;
        c.gridy = 0; c.fill = GridBagConstraints.BOTH; c.weightx = 1.0;
        classLabel.setBorder(BorderFactory.createEmptyBorder(1, 2, 1, 1));
        add(classLabel, c);

        c.gridy = 1; c.insets.bottom = 4;
        nameField.setBorder(BorderFactory.createEmptyBorder(1, 2, 1, 1));
        add(nameField, c);

        c.gridy = 0; c.gridheight = 2;
        c.gridx = 3;
        c.insets.bottom = 0;
        c.fill = GridBagConstraints.NONE;
        c.weightx = 0;
        c.insets.right = 2; c.insets.top = 3; c.insets.bottom = 3; c.insets.left = 2;
        exportButton = new TextButton(" e ", color, owner.getFont());
        exportButton.setToolTipText("Node property exports");
        exportButton.setHasBorder(false);
        exportButton.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                ChainNode node = NodeEditorGUI.this.owner.getNode();
                if (node==null) node = NodeEditorGUI.this.owner.getActualEditChain();
                new NodeExportsPopup(NodeEditorGUI.this.owner, node, new Point(exportButton.getLocationOnScreen().x, exportButton.getLocationOnScreen().y+exportButton.getWidth())).setVisible(true);
            }
        });
        add(exportButton, c);


        c.gridy = 0; c.gridheight = 2;
        c.gridx = 4;
        c.insets.bottom = 0;
        c.fill = GridBagConstraints.NONE;
        c.weightx = 0;
        c.insets.right = 2; c.insets.top = 3; c.insets.bottom = 3; c.insets.left = 2;
        infoButton = new TextButton(" i ", color, owner.getFont());
        infoButton.setToggleButton(true);
        if (owner.getNode()!=null)
            infoButton.setSelected(NodeEditorGUI.this.owner.getNode().getDebugFlag());
        infoButton.setToolTipText("Turn info flag on or off.");
        infoButton.setHasBorder(false);
        infoButton.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                boolean info = ((TextButton)e.getSource()).isSelected();
                if (NodeEditorGUI.this.owner.getNode()!=null) 
                    NodeEditorGUI.this.owner.getNode().setInfoFlag(info);
                else NodeEditorGUI.this.owner.getActualEditChain().setInfoFlag(info);
            }
        });
        add(infoButton, c);

        c.gridy = 0; c.gridheight = 2;
        c.gridx = 5;
        c.insets.bottom = 0;
        c.fill = GridBagConstraints.NONE;
        c.weightx = 0;
        c.insets.right = 2; c.insets.top = 3; c.insets.bottom = 3; c.insets.left = 2;
        debugButton = new TextButton(" d ", color, owner.getFont());
        debugButton.setToggleButton(true);
        if (owner.getNode()!=null)
            debugButton.setSelected(owner.getNode().getDebugFlag());
        debugButton.setToolTipText("Turn debug flag on or off.");
        debugButton.setHasBorder(false);
        debugButton.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                boolean debug = ((TextButton)e.getSource()).isSelected();
                if (NodeEditorGUI.this.owner.getNode()!=null)
                    NodeEditorGUI.this.owner.getNode().setDebugFlag(debug);
                else NodeEditorGUI.this.owner.getActualEditChain().setDebugFlag(debug);
            }
        });
        add(debugButton, c);

        c.gridy = 0; c.gridheight = 2;
        c.gridx = 6;
        c.insets.bottom = 0;
        c.fill = GridBagConstraints.NONE;
        c.weightx = 0;
        c.insets.right = 2; c.insets.top = 3; c.insets.bottom = 3; c.insets.left = 2;
        removeButton = new TextButton(" x ", color, owner.getFont());
        removeButton.setToolTipText("Remove node.");
        removeButton.setHasBorder(false);
        removeButton.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                if (NodeEditorGUI.this.owner.getNode()!=null) {
                    NodeEditorGUI.this.owner.getNode().destroy();
                }
            }
        });
        add(removeButton, c);

        c.weightx = 0;
        c.gridy = 0; c.gridheight = 2;
        c.gridx = 7;
        c.fill = GridBagConstraints.NONE;
        c.insets.right = 2; c.insets.top = 3; c.insets.bottom = 3; c.insets.left = 2;
        aboutButton = new TextButton(" ? ", color, owner.getFont());
        aboutButton.setToolTipText("Module help.");
        aboutButton.setHasBorder(false);
        aboutButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                NodeEditorGUI.this.owner.sendAboutMsg();
            }
        });
        add(aboutButton, c);

        c.gridheight = 1;
        c.gridx = 0;
        c.gridy = 2;
        c.weighty = 1;
        c.weightx = 1;
        c.gridwidth = 8;
        c.fill = GridBagConstraints.BOTH;
        c.anchor = GridBagConstraints.WEST;
        c.insets.right = 0; c.insets.top = 0; c.insets.bottom = 0; c.insets.left = 0;
        this.add(scroll, c);

        this.setColor(color);
        this.validate();

    }

    public void updateName() {
        if (owner.getNodeName()!=null) {
            setNode(owner.getNode());
        }
    }

    public void setColor(Color c) {
        this.color = ColorParser.xor(c);
        this.setBackground(Color.BLACK);
        if (scroll != null) {
            scroll.setBackground(Color.BLACK);
            scroll.getHorizontalScrollBar().setUI(new ForkScrollBarUI(color, color.darker()));
            scroll.getVerticalScrollBar().setUI(new ForkScrollBarUI(color, color.darker()));
            //scroll.setBorder(new LineBorder(color.darker(), 1));
            scroll.setViewportBorder(new LineBorder(color.darker().darker(), 1));
        }
        if (nameField!=null) {
            nameField.setForeground(ColorParser.xor(color));
            nameField.setCaretColor(ColorParser.xor(color));
        } if (classLabel!=null)
            classLabel.setForeground(ColorParser.xor(color));
        if (exportButton!=null) exportButton.setColor(color);
        if (infoButton!=null) infoButton.setColor(color);
        if (debugButton!=null) debugButton.setColor(color);
        if (aboutButton!=null) aboutButton.setColor(color);
        if (removeButton!=null) removeButton.setColor(color);
        if (propsPanel != null) propsPanel.setColor(color);
        if (sendMsgBox != null) sendMsgBox.setColor(color.darker());
        if (sendLabel != null) sendLabel.setForeground(color);
    }

    @Override
    public void setFont(Font font) {
        super.setFont(font);
        if (aboutButton!=null) aboutButton.setFont(font.deriveFont((float)(font.getSize()*1.2)));
        if (removeButton!=null) removeButton.setFont(font.deriveFont((float)(font.getSize()*1.2)));
        if (exportButton!=null) exportButton.setFont(font.deriveFont((float)(font.getSize()*1.2)));
        if (infoButton!=null) infoButton.setFont(font.deriveFont((float)(font.getSize()*1.2)));
        if (debugButton!=null) debugButton.setFont(font.deriveFont((float)(font.getSize()*1.2)));
        if (propsPanel != null) propsPanel.setFont(font);
        if (sendMsgBox != null) sendMsgBox.setFont(font);
        if (sendLabel != null) sendLabel.setFont(font.deriveFont((float)(font.getSize()*0.9)));
    }

    public void setNode(ChainNode node) {
        if (node == null) {
            if (owner.getActualEditChain()!=null) {
                setNode(owner.getActualEditChain());
                return;
            }            
        } else {
            classLabel.setText(node.getNodeClassName());
            nameField.setText(node.getName());
            propsPanel.setNode(node);
        }

        if (node==null || node instanceof Chain)
            aboutButton.setEnabled(false);
        else aboutButton.setEnabled(true);

        if (node==null || node.getParentChain()==null)
            exportButton.setEnabled(false);
        else exportButton.setEnabled(true);
        
        if (node instanceof Chain) {
            ((Chain)node).getVarTable().addListener(chainPropertyUpdater);
        }

        if (node!=null) {
            debugButton.setSelected(node.getDebugFlag());
            infoButton.setSelected(node.getInfoFlag());
        }
    }

    @Override
    public void setSize(int width, int height) {
        super.setSize(width, height);
        scroll.setSize(width, height);
    }

    class ChainPropertyUpdater implements VarTableListener {

        public void globalMessageEvent(Variable message) {}

        public void setVarEvent(Variable message) {}

        public void removeVarEvent(Variable var) {
            setNode(owner.getNode());
        }

        public void varScopeChanged(Variable var, boolean isPublic) {
            setNode(owner.getNode());
        }
    }
}

class PropsPanel extends JPanel implements NodeListener {

    private ChainNode node;
    private List<PropertyEditor> editors = new ArrayList<PropertyEditor>();
    private Color color;
    private NodeEditor owner;

    public PropsPanel(Color color, NodeEditor owner) {
        this.setLayout(new GridBagLayout());
        this.color = color;
        this.owner = owner;
    }

    public void setNode(ChainNode node) {
//        try { throw new Exception(""); } catch (Exception e) { e.printStackTrace(); }

        if (this.node!=null && this.node!=node) this.node.removeNodeListener(this);
        this.node = node;
        if (this.node!=null && this.node!=node) node.addNodeListener(this);
        clear();
        if (node == null) {
            this.revalidate();
            repaint();
            return;
        } 
        GridBagConstraints c = new GridBagConstraints();
        c.gridx = 0;
        c.fill = GridBagConstraints.HORIZONTAL;
        c.weightx = 1;
        c.weighty = 0;
        c.gridy = 0;
        c.insets = new Insets(2, 1, 2, 1);
        
        for (PropertyInfo prop : node.getNodeInfo().getProperties()) {
            PropertyEditor editor = new PropertyEditor(prop, node, color, owner);
            editor.setFont(getFont());
            editors.add(editor);
            this.add(editor, c);
            c.gridy = c.gridy + 1;
        }
        JPanel hole = new JPanel();
        hole.setBackground(Color.BLACK);
        c.gridy = c.gridy + 1;
        c.fill = GridBagConstraints.BOTH;
        c.weightx = 1;
        c.weighty = 1;
        add(hole, c);

        this.validateTree();
        repaint();


    }

    @Override
    public void setFont(Font font) {
        super.setFont(font);
        if (editors != null) {
            for (PropertyEditor p : editors) {
                p.setFont(font);
            }
        }
    }

    public void setColor(Color color) {
        this.color = color;
        this.setBackground(Color.BLACK);
        if (editors != null) {
            for (PropertyEditor p : editors) {
                p.setColor(color);
            }
        }
    }

    private void clear() {
        this.removeAll();
        this.editors.clear();
    }

    public void messageReceived(ChainNode src, Variable var, Channel channel, long timestamp) {
    }

    public void messageSent(ChainNode src, Variable var, long timestamp) {
    }

    public void nameChanged(ChainNode src) {
        owner.getGUI().updateName();
        revalidate();
    }

    public void metaChanged(ChainNode src) {
    }

    public void connectionsChanged(ChainNode src) {
    }

    public void nodeDestroyed(ChainNode src) {
    }

    public void nodeMoved(ChainNode src) {
    }

    public void propertyChanged(ChainNode src, String name, Object value) {
    }
}



